package com.cskaoyan.javase.object._4hashcode;

/**
 * hashCode()方法的声明:
 * public native int hashCode();
 * 很显然，它是一个本地方法，这个方法也没有任何参数，返回值是一个int类型整数。
 *
 * 方法的作用:
 * 由于它是一个本地方法，我们通过查阅JDK文档，文档中关于它的作用说明了两点：
 *      1.返回该对象的哈希码值。(返回值是对象的哈希码值)
 *      2.支持此方法是为了提高哈希表（例如 java.util.Hashtable 提供的哈希表）的性能。
 *
 * 哈希: 化无限为有限,将无限的集合A中的元素,唯一的对应到有限集合B上的某个元素
 * 其中对应关系就是哈希算法,集合B上被映射的元素就是哈希值
 *
 * Java当中的哈希映射:
 *      对应关系就是hashCode方法的实现,hashCode就是哈希算法
 *      集合A是某个类的对象的集合,理论来说是无限
 *      集合B就是int数据类型,它是有限的
 *      hashCode方法的返回值就是哈希值.
 *
 * 哈希冲突越多,就意味着哈希表的性能越差,为了让哈希表的性能更好
 * 所以要设计出优秀的哈希算法,尽量少出现哈希冲突,这就是第二个作用.
 *
 * 上面hashCode方法的作用和equals方法完全没有关系,但实际上这两个方法,关系十分紧密
 *
 * 哈希表是一种存储键值对的数据结构,在哈希表当中,key是唯一性标识,不存在两个相同的key
 * key也是一个对象,那么如何判断两个key相等,又怎么知道两个key不相等呢?
 * 用equals方法
 * 如果equals方法判断返回true,这两个对象相等,哈希表中不能存两个相同的key,这时要么覆盖原先的键值对,要么选择不存储
 * 这时这两个key是相同的对象,那么它们就相当于哈希映射当中,集合A中的同一个对象
 * 既然是同一个元素,那么在哈希映射中,这两个对象的哈希值就必须是一样的
 * -->
 *      如果equals方法比较两个对象返回true,那么这两个对象的哈希值应该是一定相等的,这样才符合映射的定义
 *      实操中,哈希表的存储才不会出现问题
 *      如果一个键值对计算哈希值存储在下标3的位置,现在新来了一个键值对,key是重复的(equals方法比较两个key对象返回true)
 *          新的键值对就需要覆盖掉原先的键值对
 *          这时如果它们key的哈希值不同,就会导致新的键值对存储位置不在下标3,这是矛盾了.
 *
 * -->
 *      equals方法能够控制哈希值,equals方法返回true,哈希值必须相同
 *      这要求这两个方法,判断对象相等/计算哈希值,它们的条件必须是一样的,如果条件不一样,就保证不了上述原则
 *
 *      equals方法的默认实现,是比较地址判断对象相等的
 *      hashCode方法的默认实现,也是根据对象的地址计算哈希值的
 *      所以默认情况下,这两个方法可以保证equals true,哈希值完全相同.
 *      而且一旦重写equals方法,也一定要同时重写hashCode方法
 *      而且重写的条件必须是一样的
 *      比如equals方法按照age,score这两个属性的取值重写,那么hashCode也一样需要用这两个属性取值重写.
 *
 *      实际开发中,使用idea的模板自动重写hashCode方法
 *      保持它的重写和equals方法具有相同的条件就可以了
 *
 *------------------>
 * 如果equals方法比较两个对象返回false,那么它们是集合A上的不同元素,这时不强制要求,它们的哈希值完全不同(实际上哈希冲突一定存在,一定会出现多对一)
 * 但是"人要相信光,要追求美好的事物"
 * 程序员应该知道设计更好的哈希算法,尽量减少哈希冲突,能够尽量提高哈希表的性能.
 *
 * 注意事项:
 *      toString方法默认实现会调用hashCode方法
 *      这时注意,一旦重写了hashCode方法
 *      toString方法打印的就不是地址值
 *
 *      所以建议重写hashCode方法的类
 *      要么不用toString
 *      要么也一起重写
 *
 * @since 14:54
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Student s1 = new Student(18, 750);
        Student s2 = new Student(18, 750);
        Student s3 = s1;
        System.out.println(s1.equals(s3));
        System.out.println(s1.hashCode());
        System.out.println(s3.hashCode());
        System.out.println("-----------------");
        System.out.println(s1.equals(s2));
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

        System.out.println("------------------------");
        Student s4 = new Student(29, 700);
        System.out.println(s4.equals(s1));
        System.out.println(s1.hashCode());
        System.out.println(s4.hashCode());

        System.out.println("--------------------");
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
        System.out.println(s4);
    }
}

class Student {
    int age;
    double score;

    public Student(int age, double score) {
        this.age = age;
        this.score = score;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return Double.compare(student.score, score) == 0;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", score=" + score +
                '}';
    }
}